package com.pzj.export;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPRow;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.pzj.export.config.Align;
import com.pzj.export.config.BaseConfig;
import com.pzj.export.config.CellColConfig;
import com.pzj.export.config.ColHeaderColConfig;
import com.pzj.export.config.ColHeaderConfig;
import com.pzj.export.config.ColHeaderRowConfig;
import com.pzj.export.config.FooterConfig;
import com.pzj.export.config.HeaderConfig;
import com.pzj.export.config.TableConfig;
import com.pzj.export.config.TitleConfig;

/**
 * 按配置导出Pdf文件。
 * 
 * @author liulm
 * 
 */
public class ExportPdf<T> extends ExportRowDataBaseHolder {
	/**
	 * 导出pdf文件流
	 * 
	 * @param objList
	 *            包含需导出数据的对象list。
	 * @param tableConfig
	 *            导出表格配置。
	 * @param pageSize
	 *            输出页面设置。
	 * @param out
	 *            输出流。
	 * @return 成功返回导出条数，失败返回-1。
	 */
	public int exportPdf(List<T> objList, TableConfig<T> tableConfig,
			Rectangle pageSize, OutputStream out) {
		if (objList == null)
			throw new IllegalArgumentException("导出数据list不能为null！");

		if (tableConfig == null)
			throw new IllegalArgumentException("导出配置不能为null！");

		if (out == null)
			throw new IllegalArgumentException("输出流不能为null！");

		int result = -1;

		List<CellColConfig<T>> cellColConfigList = tableConfig.getCellCols();
		if (cellColConfigList == null || cellColConfigList.size() == 0)
			throw new IllegalArgumentException("没有单元列配置！");

		// 获得显示列的配置。
		CellColConfig<T>[] cellColArr = CellColConfig
				.getShowCols(cellColConfigList);

		// 添加序号头
		if (tableConfig.isShowSerialNumber()) {
			cellColArr = addSerialNumber(cellColArr);
		}

		int totalColNum = cellColArr.length;

		Document document = new Document(pageSize);
		try {
			PdfWriter.getInstance(document, out);
			document.open();
			PdfPTable table = new PdfPTable(totalColNum);
			// 记录当前操作的行数
			int currentOutputRow = 0;
			// title
			if (tableConfig.getTitle() != null
					&& tableConfig.getTitle().getShowLocation() == TitleConfig.TOP) {
				currentOutputRow += setTitle(tableConfig.getTitle(),
						totalColNum, currentOutputRow, table);
			}

			PdfPRow pdfPRow = table.getRow(0);
			pdfPRow.setMaxHeights(400);

			// header
			currentOutputRow += setHeader(tableConfig.getHeader(), totalColNum,
					currentOutputRow, table);

			// 根据设置的列头输出
			currentOutputRow += setColHeader(tableConfig, currentOutputRow,
					tableConfig.isShowSerialNumber(), totalColNum, cellColArr,
					table);

			// 表格数据
			result = setTableData(objList, currentOutputRow, cellColArr,
					tableConfig.isShowSerialNumber(), table);
			currentOutputRow += result;
			// footer
			currentOutputRow += setFooter(tableConfig.getFooter(), totalColNum,
					currentOutputRow, table);

			if (tableConfig.getHeaderRows() > 0)
				table.setHeaderRows(tableConfig.getHeaderRows());

			document.add(table);

			// title
			if (tableConfig.getTitle() != null
					&& tableConfig.getTitle().getShowLocation() == TitleConfig.BOTTOM) {
				setTitleBottom(tableConfig.getTitle(), document);
			}
		} catch (DocumentException de) {
			result = -1;
			System.err.println(de.getMessage());
		} catch (IOException e) {
			result = -1;
			e.printStackTrace();
		} finally {
			document.close();
		}

		return result;
	}

	/**
	 * 设置标题在底部
	 * 
	 * @param titleConfig
	 * @param document
	 * @throws DocumentException
	 * @throws IOException
	 */
	private void setTitleBottom(TitleConfig titleConfig, Document document)
			throws DocumentException, IOException {
		Paragraph paragraph = getBlankLine();
		document.add(paragraph);

		Font fontChinese = createFont(titleConfig);
		paragraph = new Paragraph(titleConfig.getContent(), fontChinese);
		setAlignment(titleConfig.getAlign(), paragraph);
		document.add(paragraph);
	}

	/**
	 * 获得空白行
	 * 
	 * @return
	 */
	private Paragraph getBlankLine() {
		Paragraph paragraph = new Paragraph(" ");
		paragraph.setAlignment(Element.ALIGN_CENTER);
		return paragraph;
	}

	/**
	 * 设置底部
	 * 
	 * @param footerConfig
	 * @param totalColNum
	 * @param currentOutputRow
	 * @param table
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	private int setFooter(FooterConfig footerConfig, int totalColNum,
			int currentOutputRow, PdfPTable table) throws DocumentException,
			IOException {
		int currentRow = currentOutputRow;
		if (footerConfig != null) {
			Font fontChinese = createFont(footerConfig);

			PdfPCell cell = createPdfPCell(footerConfig);

			cell.setColspan(totalColNum);
			String content = footerConfig.getContent();
			cell.setPhrase(new Paragraph(content, fontChinese));

			table.addCell(cell);

			currentRow++;
		}
		return currentRow - currentOutputRow;
	}

	/**
	 * 
	 * @param objList
	 * @param currentOutputRow
	 * @param cellColArr
	 * @param showSerialNumber
	 * @param table
	 * @return
	 * @throws IOException
	 * @throws DocumentException
	 */
	private int setTableData(List<T> objList, int currentOutputRow,
			CellColConfig<T>[] cellColArr, boolean showSerialNumber,
			PdfPTable table) throws DocumentException, IOException {
		int currentRow = currentOutputRow;
		// 已输出的行数

		int serialNumber = 0;
		Iterator<T> objIter = objList.iterator();
		while (objIter.hasNext()) {
			T recordObj = objIter.next();

			String[] recordArr = null;

			if (showSerialNumber)
				recordArr = getOneRowDataAddSerialNumber(recordObj, cellColArr,
						String.valueOf(++serialNumber));
			else
				recordArr = getOneRowData(recordObj, cellColArr);

			for (int i = 0; i < cellColArr.length; i++) {
				Font fontChinese = createFont(cellColArr[i]);

				PdfPCell cell = createPdfPCell(cellColArr[i]);

				cell.setPhrase(new Paragraph(recordArr[i], fontChinese));

				table.addCell(cell);
			}
			currentRow++;
		}

		return currentRow - currentOutputRow;
	}

	/**
	 * 
	 * @param tableConfig
	 * @param currentOutputRow
	 * @param cellColArr
	 * @param table
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	private int setColHeader(TableConfig<T> tableConfig, int currentOutputRow,
			boolean showSerialNumber, int totalColNum,
			CellColConfig<T>[] cellColArr, PdfPTable table)
			throws DocumentException, IOException {
		int currentRow = currentOutputRow;
		if (tableConfig.getColHeader() != null) {
			currentRow = setComplexColHeader(tableConfig.getColHeader(),
					currentRow, showSerialNumber, totalColNum, table);

		}
		// 如果未设置列头，根据列配置中的列头名显示。
		else {
			currentRow = setSimpleColHeader(tableConfig, cellColArr,
					currentRow, table);
		}
		return currentRow - currentOutputRow;
	}

	/**
	 * 根据配置生成复合列头。
	 * 
	 * @param colHeaderConfig
	 * @param currentRow
	 * @param table
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	private int setComplexColHeader(ColHeaderConfig colHeaderConfig,
			int currentRow, boolean showSerialNumber, int totalColNum,
			PdfPTable table) throws DocumentException, IOException {
		// 自动生成序号列头
		ColHeaderColConfig colHeaderColConfig1 = colHeaderConfig
				.getColHeaderRowConfig(0).getColHeaderColConfig(0);

		Font fontChinese = createFont(colHeaderColConfig1);

		PdfPCell serialNumberCell = createPdfPCell(colHeaderColConfig1);
		serialNumberCell.setPhrase(new Paragraph("序号", fontChinese));

		if (showSerialNumber) {
			table.addCell(serialNumberCell);
			totalColNum--;
		}

		PdfPTable subTable = new PdfPTable(totalColNum);

		serialNumberCell = new PdfPCell(subTable);
		serialNumberCell.setColspan(totalColNum);
		table.addCell(serialNumberCell);

		// 配置复杂colheader.
		List<ColHeaderRowConfig> rows = colHeaderConfig.getRows();
		for (int rowIndex = 0, rowSize = rows.size(); rowIndex < rowSize;) {
			ColHeaderRowConfig colHeaderRowConfig = rows.get(rowIndex);
			// 一行中列配置集合的最大合并行数。
			int maxRowspan = 1;
			if (colHeaderRowConfig.getCols().size() == 1) {
				ColHeaderColConfig colHeaderColConfig = colHeaderRowConfig
						.getColHeaderColConfig(0);

				PdfPCell cell = getPdfPCell(colHeaderColConfig);

				subTable.addCell(cell);

				currentRow++;
			} else {

				List<ColHeaderColConfig> cols = colHeaderRowConfig.getCols();

				for (int colIndex = 0, colSize = cols.size(); colIndex < colSize; colIndex++) {
					int rowspan = cols.get(colIndex).getRowspan();
					if (rowspan > maxRowspan)
						maxRowspan = rowspan;
				}

				// 当全部为一行时
				if (maxRowspan == 1) {
					for (int colIndex = 0, colSize = cols.size(); colIndex < colSize; colIndex++) {
						PdfPCell cell = getPdfPCell(cols.get(colIndex));

						subTable.addCell(cell);
					}
				} else {
					// 当前子表包含列配置索引
					int curColConfigIndex[] = null;

					// 行按列的顺序生成
					for (int colIndex = 0, colSize = cols.size(); colIndex < colSize; colIndex++) {
						if (colIndex == 0) {
							curColConfigIndex = new int[maxRowspan];
						}

						ColHeaderColConfig colHeaderColConfig = cols
								.get(colIndex);

						int curRow = 0;
						curColConfigIndex[curRow]++;
						// 当列的合并行数和最大数相等时，就为一个单元格。
						if (colHeaderColConfig.getRowspan() == maxRowspan) {
							PdfPCell cell = getPdfPCell(colHeaderColConfig);

							subTable.addCell(cell);
						}
						// 创建table
						else {
							PdfPCell cell = getPdfPCellEmbedTable(
									colHeaderColConfig, rows, maxRowspan,
									rowIndex, curRow + 1, curColConfigIndex);

							subTable.addCell(cell);
						}
					}
				}
			}

			// 以第一个列的行数设为一行。
			rowIndex += maxRowspan;

		}
		return currentRow;
	}

	/**
	 * 单元格内部嵌入一个表格。每个表格都只有两行，如果表格不止两行，第二行开始的其他行包含到另一个表格中。
	 * 
	 * @param colHeaderColConfig
	 *            表格第一行配置
	 * @param rows
	 *            整个导出复合表格头的行配置。
	 * @param maxRowspan
	 *            表格的最大行数
	 * @param rowIndex
	 *            获得行配置的基准索引
	 * @param nextRow
	 *            取得表格第二行配置的相对于基准索引的偏移量。
	 * @param curColConfigIndex
	 *            对应行还未设置的第一个列的配置索引。
	 * 
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	private PdfPCell getPdfPCellEmbedTable(
			ColHeaderColConfig colHeaderColConfig,
			List<ColHeaderRowConfig> rows, int maxRowspan, int rowIndex,
			int nextRow, int curColConfigIndex[]) throws DocumentException,
			IOException {
		PdfPTable pdfPTable = new PdfPTable(colHeaderColConfig.getColspan());

		PdfPCell cell = getPdfPCell(colHeaderColConfig);
		pdfPTable.addCell(cell);

		ColHeaderRowConfig rowConfig = rows.get(rowIndex + nextRow);

		int colspan = 0;
		int rowspan = colHeaderColConfig.getRowspan();
		do {
			ColHeaderColConfig subColConfig = rowConfig
					.getColHeaderColConfig(curColConfigIndex[nextRow]++);
			colspan += subColConfig.getColspan();
			if (subColConfig.getRowspan() + rowspan == maxRowspan) {
				cell = getPdfPCell(subColConfig);
			} else {
				cell = getPdfPCellEmbedTable(subColConfig, rows, maxRowspan
						- rowspan, rowIndex, nextRow + 1, curColConfigIndex);
			}
			pdfPTable.addCell(cell);

		} while (colspan < colHeaderColConfig.getColspan());

		cell = new PdfPCell(pdfPTable);
		cell.setColspan(colHeaderColConfig.getColspan());

		return cell;
	}

	/**
	 * 获得按配置设置好的PdfPCell对象
	 * 
	 * @param colHeaderColConfig
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	private PdfPCell getPdfPCell(ColHeaderColConfig colHeaderColConfig)
			throws DocumentException, IOException {
		Font fontChinese = createFont(colHeaderColConfig);

		PdfPCell cell = createPdfPCell(colHeaderColConfig);

		cell.setColspan(colHeaderColConfig.getColspan());

		String content = colHeaderColConfig.getContent();
		cell.setPhrase(new Paragraph(content, fontChinese));

		return cell;
	}

	/**
	 * 生成简单表头
	 * 
	 * @param tableConfig
	 * @param cellColArr
	 * @param currentRow
	 * @param table
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	private int setSimpleColHeader(TableConfig<T> tableConfig,
			CellColConfig<T>[] cellColArr, int currentRow, PdfPTable table)
			throws DocumentException, IOException {
		// 设置列头的格式
		BaseFont bfChinese = BaseFont.createFont("STSong-Light",
				"UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

		Font fontChinese = new Font(bfChinese, 10, Font.NORMAL);

		float[] widths = new float[cellColArr.length];
		// 输出列头
		for (int col = 0; col < cellColArr.length; col++) {
			widths[col] = getPdfWidth(cellColArr[col].getWidth());
			PdfPCell cell = new PdfPCell(new Paragraph(
					cellColArr[col].getColHeader(), fontChinese));
			cell.setHorizontalAlignment(Element.ALIGN_CENTER);

			if (tableConfig.isColHeaderBackgroundGray()) {
				cell.setBackgroundColor(BaseColor.LIGHT_GRAY);
			}
			table.addCell(cell);

		}

		table.setWidths(widths);

		if (getPdfWidth(tableConfig.getTotalWidth()) > 0)
			table.setTotalWidth(getPdfWidth(tableConfig.getTotalWidth()));
		else
			table.setTotalWidth(widths);

		table.setLockedWidth(tableConfig.isLockedWidth());

		currentRow++;
		return currentRow;
	}

	/**
	 * 设置头
	 * 
	 * @param headerConfig
	 * @param totalColNum
	 * @param currentOutputRow
	 * @param table
	 * @return
	 * @throws IOException
	 * @throws DocumentException
	 */
	private int setHeader(HeaderConfig headerConfig, int totalColNum,
			int currentOutputRow, PdfPTable table) throws DocumentException,
			IOException {
		int currentRow = currentOutputRow;
		if (headerConfig != null) {
			Font fontChinese = createFont(headerConfig);

			PdfPCell cell = createPdfPCell(headerConfig);

			cell.setColspan(totalColNum);
			String content = headerConfig.getContent();
			cell.setPhrase(new Paragraph(content, fontChinese));

			table.addCell(cell);

			currentRow++;
		}
		return currentRow - currentOutputRow;
	}

	/**
	 * 设置标题
	 * 
	 * @param titleConfig
	 * @param totalColNum
	 * @param currentOutputRow
	 * @param table
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	private int setTitle(TitleConfig titleConfig, int totalColNum,
			int currentOutputRow, PdfPTable table) throws DocumentException,
			IOException {
		int currentRow = currentOutputRow;
		if (titleConfig != null) {
			Font fontChinese = createFont(titleConfig);

			PdfPCell cell = createPdfPCell(titleConfig);

			cell.setColspan(totalColNum);
			cell.setUseDescender(true);

			String content = titleConfig.getContent();
			content += "\n ";// 在标题后加空行
			cell.setPhrase(new Paragraph(content, fontChinese));

			table.addCell(cell);

			currentRow++;
		}
		return currentRow - currentOutputRow;
	}

	/**
	 * 创建字体
	 * 
	 * @param baseConfig
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	private Font createFont(BaseConfig baseConfig) throws DocumentException,
			IOException {
		BaseFont bfChinese = BaseFont.createFont("STSong-Light",
				"UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

		Font fontChinese = new Font(bfChinese, 10, Font.NORMAL);
		fontChinese.setSize(baseConfig.getFontPointSize());

		if (baseConfig.isFontBold())
			fontChinese.setStyle(Font.BOLD);

		return fontChinese;
	}

	/**
	 * 创建cell
	 * 
	 * @param baseConfig
	 * @return
	 */
	private PdfPCell createPdfPCell(BaseConfig baseConfig) {
		PdfPCell cell = new PdfPCell();
		if (baseConfig.isBackgroundGray()) {
			cell.setBackgroundColor(BaseColor.LIGHT_GRAY);
		}

		setAlignment(baseConfig.getAlign(), cell);

		cell.setNoWrap(!baseConfig.isWrap());
		if (baseConfig.isBorder())
			cell.setBorderWidth(1);
		else
			cell.setBorderWidth(0);

		return cell;
	}

	/**
	 * 设置的排列方式。
	 * 
	 * @param align
	 * @param paragraph
	 * 
	 */
	private void setAlignment(Align align, Paragraph paragraph) {
		if (align == Align.LEFT) {
			paragraph.setAlignment(Element.ALIGN_LEFT);
		} else if (align == Align.CENTER) {
			paragraph.setAlignment(Element.ALIGN_CENTER);
		} else if (align == Align.RIGHT) {
			paragraph.setAlignment(Element.ALIGN_RIGHT);
		}
	}

	/**
	 * 设置单元格式的排列方式。
	 * 
	 * @param align
	 * @param cell
	 * 
	 */
	private void setAlignment(Align align, PdfPCell cell) {
		if (align == Align.LEFT) {
			cell.setHorizontalAlignment(Element.ALIGN_LEFT);
		} else if (align == Align.CENTER) {
			cell.setHorizontalAlignment(Element.ALIGN_CENTER);
		} else if (align == Align.RIGHT) {
			cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
		}
	}

	/**
	 * 转换为pdf的宽度。
	 * 
	 * @param width
	 * @return
	 */
	private float getPdfWidth(float width) {
		return (float) (width * 72 / 25.4);
	}

//	/**
//	 * 转换为pdf的高度。
//	 * 
//	 * @param height
//	 * @return
//	 */
//	private float getPdfHeight(float height) {
//		return height;
//	}
}
